逻辑门
先来认识(记)一下基础的逻辑门:

注意,NAND,NOR那里的那个小圆圈代表的是取反(negation),跟最底下的Negation是一个效果。
比如这里表达的是 $y = \neg (a \wedge \neg b) $ :

一般情况下堵在逻辑门出入口的圆圈指的是取反,而下面这种只是表示线路连接而已:

不要弄混了。
加法(Addition)
半加器(Der Halbaddierer ,HA)
半加器的目的是计算2个bit的加法,输出也为2个bit,即:
1 | a0 |
也可以用数学公式表达:
对应的真值表则是:
也就是说
对应的逻辑电路图:


左边是and,右边是xor。在之后的内容里用HA指代半加器。
Kosten和Tiefe分别是:C(HA) = 2, Depth(HA) = 1。
全加器(Der Volladdierer ,FA)
全加器的目的是计算3个bit的加法,输出为2个bit,即:
1 | a0 |
其中c是进位信息。
对应的数学表达:
真值表:
注意到:
(ha0表示2个数相加得到的s0位的值,ha1同理。)
逻辑电路图:

Kosten和Tiefe分别是:C(HA) = 5, Depth(HA) = 3。
选择器(Multiplexer,MUX)
选择器正如字面意思,会从2个数里选择一个数。数学表达:
注意到:
逻辑电路图:

后续一般用这个符号指代:


n-bit的选择器需要:
- 3n+1个门
- 深度为3
乘法

或者

存储(Speichern)
假如说我们把一个门的输出再连回输入的口会发生什么呢?

假设一开始a,y都是0。只要a抱持0,y自然也会保持0。但如果a变成了1的话,y也会跟着变成1,并且之后不管发生什么了都会一直是1。
也就是说它实现了一个最简易的存储功能。(只要a在任何时间点变成了1,那么y就会一直记得这件事情。)
当然,想要实现更完善的存储功能,我们还得加上复原的选项。
RS-Latch / RS-FlipFlop
S (Set),R (Reset)。

真值表:
我们关心的是在某个输入固定时最终会落到的稳定态,所以计算顺序本身不是逻辑因果。以S=1,R=0的情况为例:我们可以先计算出$\neg Q(t)$一定等于0,然后再用这个结果去计算得到$Q(t)$等于1。这个时候可以把Q等于1再带进去检查$\neg Q(t)$是否等于0。
相当于我们是在找一组方程式的解。
D-Latch / D-FlipFlop
我们想要一个更加方便的结构:
- 一个负责传输数据(q)
- 一个负责控制这个数据是否会被写入
D(Data),E (Enable)。

(设上面 and 的结果为S,下面 and 的结果为R,跟RS-Latch的位置一样。)
真值表:
也就是说:
- 当E为0时,不发生任何改变/存储
- 当E为1时,会存储D当前的值/内容

E=1的时候D和Q的值是一模一样的。E=0的时候就直接把Q当前的延长出去到下一个E=1就好。
Taktflankengesteuerte D-Flipflops

假设原来CLK=0。
1. CLK 刚从 0 跳到 1(上升沿瞬间):E 变为 1(打开 D-Latch)。
- 直接路径:CLK 立刻 = 1
- 延迟路径:CLK_delayed 还没来得及变成 1,仍≈0
所以 $\neg CLK_{delayed}\approx 1$
于是:
2. 过了延迟 Δt 后:E 关掉,D-Latch 再次锁住。
延迟链输出终于变成 1:
- CLK_delayed = 1
- $\neg CLK_{delayed}=0$
于是:
3. CLK 从 1 掉到 0(下降沿):下降沿不会产生脉冲。
直接 CLK=0,则无论另一边是什么:
所以就是只在CLK上升后的一小段实际内会存储D的内容。

Clk = 0 时:
- 左边 E=/Clk=1 → 左边(master)透明,N 跟随 D_in
- 右边 E=Clk=0 → 右边(slave)锁住,Q_out 保持不变
Clk = 1 时:
- 左边 E=/Clk=0 → 左边锁住,N 固定
- 右边 E=Clk=1 → 右边透明,Q_out 跟随 N(但 N 这时是固定的)
和之前类似的画图策略:
- 先画N,因为有negation的存在,所以CLK=0时,N的部分和D的部分一模一样。
- 再画Q。CLK=1时,Q和D一模一样。
2*2 Bit Speicher

write位等于0代表读,等于1代表写;
灰色的是选择器;
上面的2个黄色部分是A (Adresse)=0的结果,下面的是A (Adresse)=1的。
Inkrementer
自增器主要用于比如说更新程序计数器(Program counter),需要到达的效果类似:
1 | add pc, pc, 4 |
需要2个步骤,计算加法并且存储结果。所以对应的逻辑电路为:

RISC-V Single-Cycle Prozessor
部件
先解释一下所有需要的部件:
Program Counter (PC):

Instruktionsspeicher:存储所有需要执行的指令

- A:Addresse
- RD(Read Data):读取的指令
Registerbank:

会处理所有的寄存器。其中
- A1,A2是操作里的寄存器的地址。
- RD1和RD2是这两个寄存器里的具体内容。
- A3和WD3是第3个寄存器(存储计算结果的)。
- 只有当WE3=1(Write Enable)的时候才会写入第三个寄存器。
Arithmetic Logic Unit (ALU):

详细版:

ALU会进行所有的逻辑运算。ALUControl这个参数会指定执行具体哪项操作(如ADD, AND, SUB 等,以3 bit的格式)。注意,最后一个bit的内容(即 $\text{ALUControl}_0$)会传递给上面的Sum以及Sum上面的那个MUX(选择器)。
比如:
- 000表示加法;
- 001表示减法
- 010表示AND;
- 011表示OR;
- 101表示进行是否等于0的判断。
Datenspeicher:

- CLK表示系统内的计时器。
- A 是需要读取/写入的地址。
- RD是读取到的信息(Read Data)。
- WD是需要写入的信息(Write Data)。
- 只有当WE=1(Write Enable)的时候才会写入。
RISC-V 架构
完整的RISC-V Single-Cycle Prozessor的结构:

在一个时钟周期内,信号从头到尾在整个硬件电路里跑完完整的一轮。
解释一下这些信号:
PCSrc:决定当前地址,是继续下去还是回到返回地址。
- 0表示回到之前的地址继续
- 1表示在现在的地址基础上继续。
ResultSrc:决定当前输出。
- 如果是lw之类的操作,需要设为01,因为需要将读取到的信息存入结果寄存器(RD -> WD3)。
- 如果是R-Type之类的操作,则需要设为00。
MemWrite:决定当前是否要写入到Memory中。
- 0表示不写入;
- 1表示写入。
ALUControl:决定当前的ALU操作
add, and, sub 之类的R-Type操作直接设为操作本身就好(以3 bit的格式);
lw, sw需要设为add,因为需要通过加法计算地址;
beq等需要设为sub,因为这里判断a和b是否相等是通过直接传递a-b的计算结果实现的。

ALUSrc:决定当前是使用的第二个寄存器还是立即数(imm)。
- 0表示使用第二个寄存器的内容;
- 1表示使用imm。
ImmSrc:决定当前读取imm的格式,因为每种Type的指令存储imm的格式都不一样。

RegWrite:决定当前结果是否需要写入第3个寄存器。
- 0表示不写入;
- 1表示写入。
ControlUnit的部分构造稍微再具体一点是这样的:

一些常见操作的参数例子:

x表示不重要。比如说在 jal 那里,jump 的参数会被设为1,这个1会和 (zero AND Branch) 进行 or 操作,所以结果一定是1,也就意味着 Branch的结果在这个情况下无关紧要。
RISC-V Multicycle Prozessor
在Singlecycle里,takt的时间需要考虑最复杂的指令执行所需要的时间,比如说load之类的操作比其他的操作要久很多,所以整体看下来很亏,因为需要将就这种操作。
在多周期 RISC-V 架构中,一条指令的执行被拆分为多个时钟周期,每个周期完成一个特定的阶段(Stage),并通过CLK控制周期。takt的时间只需要设置成这些步骤中最长的时间即可。
通常分为以下 5 个阶段:
1. Fetch(取指 + 设置 PC)
- 动作:根据 PC 寄存器中的地址,从内存(Instr/Data Memory)中读取指令代码,并将其存入指令寄存器(Instr)中。
- 同时进行:利用 ALU 计算
PC + 4,并将新地址写回 PC 寄存器,为取下一条指令做准备。
2. Decode(译码)
- 动作:控制单元(Control Unit)解析指令寄存器(Instr)里的位(如
op,funct3等),确定指令类型。 - 数据准备:从寄存器堆(Register File)中读出源操作数
rs1和rs2的值,分别存入中间寄存器 A 和 B 中;同时Extend部件生成扩展后的立即数ImmExt。
3. Execute(执行)
- 动作:ALU 根据控制信号执行具体的计算任务。
- 算术指令(如
addi):计算A + ImmExt。 - 访存指令(如
lw/sw):计算内存基地址加偏移量A + ImmExt,结果存入ALUOut。 - 分支指令(如
beq):计算跳转目标地址并比较寄存器值。
- 算术指令(如
4. Memory(访存)
- 注意:只有Load和Store指令需要这个阶段。
- 动作:
lw(Load):使用 ALU 计算出的地址从内存中读取数据,存入Data寄存器。sw(Store):将寄存器 B 中的值写入 ALU 计算出的内存地址中。
- 算术指令(如
addi)通常跳过此阶段或在此阶段不执行有效操作。
5. Writeback(写回)
- 动作:将最终结果写回到寄存器堆的
rd寄存器中。- 对于算术指令:写回的是
ALUOut里的运算结果。 - 对于Load 指令:写回的是从内存读出的
Data。
- 对于算术指令:写回的是
部件

与之前不同,这里把Intruktionsspeicher和Datenspeicher合并了。
并且这里PC多加了一个EN信号,起到控制的作用。
架构

可以把里面带有EN信号的部件理解成一个寄存器。
1. 存储与指令流控制(Memory & Instruction)
这组信号负责协调内存访问以及指令的锁存。
PCWrite:PC 寄存器的写使能信号。当该信号有效时,PC 会在时钟上升沿更新为PCNext的值。AdrSrc:地址来源选择信号。控制内存地址输入端的二选一选择器(Mux)。0代表选择 PC 地址(取指阶段);1代表选择Result总线上的地址(访存阶段)。
MemWrite:内存的写使能信号。1时执行写内存操作(如sw指令);0时执行读操作或不操作。
IRWrite:指令寄存器(Instr)的写使能信号。仅在取指周期有效,用于将从内存读出的指令编码锁存在寄存器中,供后续多个周期使用。
2. 寄存器堆与立即数控制(Register & Immediate)
这组信号控制数据的流向以及立即数的处理方式。
RegWrite:寄存器堆(Register File)的写使能信号。当需要将结果写回rd寄存器时(如addi、lw的写回阶段),该信号置为1。ImmSrc:立即数格式控制信号。由控制单元根据指令的op编码生成,告知Extend部件按哪种格式(I-type, S-type, B-type 等)提取并扩展指令中的立即数。
3. ALU 输入选择与运算控制(ALU Control)
这组信号决定了 ALU 执行的具体逻辑任务。
ALUSrcA:ALU 第一个操作数的选择信号。00:选择PC。01:选择OldPC。10:选择从寄存器读取的RD1(存储在 A 寄存器中)。
ALUSrcB:ALU 第二个操作数的选择信号。00:选择从寄存器读取的RD2(存储在 B 寄存器中)。01:选择常量4。10:选择扩展后的立即数ImmExt。
ALUControl:运算类型控制信号。直接控制 ALU 执行加、减、与、或等特定的算术或逻辑运算。
4. 结果写回与跳转控制(Result & Program Flow)
这组信号负责将处理完的数据导向正确的目的地。
ResultSrc:结果总线选择信号。控制三选一选择器,决定Result总线的数据来源:00:来自ALUOut(存储上一个周期计算结果的寄存器)。01:来自Data(存储从内存读出数据的寄存器)。10:来自当前周期的ALUResult直接输出。
PCSrc:PC 来源选择信号。决定下一个周期的 PC 值。0:通常指向ALUResult(计算出的PC+4)。1:通常指向ALUOut(计算出的跳转目标地址)。
Control Unit更细节的构造:

ALU Decoder:

Instruction Decoder:

阶段
更详细的阶段:
Decode:

MemAdr:

MemRead:

MemWB:


S-Typ:

R-Typ:


B-Typ:


I-Typ:


JAL:

完整的时序流程图
